#ifndef RAIS_META_H
#define RAIS_META_H

namespace rais{

inline namespace meta{

//meta types

class MetaNull {};

template <typename T, T Value = 0>
class MetaInteger {
	public:
	static constexpr T value = Value;
	
	template <typename Val, typename Result_T = decltype(value + Val::value)>
	struct Add{
		using result = MetaInteger<Result_T, value + Val::value>;
	};
	template <typename Val, typename Result_T = decltype(value - Val::value)>
	struct Mns{
		using result = MetaInteger<Result_T, value - Val::value>;
	};
	template <typename Val, typename Result_T = decltype(value * Val::value)>
	struct Mul{
		using result = MetaInteger<Result_T, value * Val::value>;
	};
	template <typename Val, typename Result_T = decltype(value / Val::value)>
	struct Div{
		using result = MetaInteger<Result_T, value / Val::value>;
	};
	template <typename Val, typename Result_T = decltype(value % Val::value)>
	struct Rmd{
		using result = MetaInteger<Result_T, value % Val::value>;
	};
	
	struct Inc {
		using result = MetaInteger<T, value + 1>;
	};
	struct Dec {
		using result = MetaInteger<T, value - 1>;
	};
	
	//bit operator
	template <typename Val, typename Result_T = decltype(value & Val::value)>
	struct BitAnd{
		using result = MetaInteger<Result_T, value & Val::value>;
	};
	template <typename Val, typename Result_T = decltype(value | Val::value)>
	struct BitOr{
		using result = MetaInteger<Result_T, value | Val::value>;
	};
	template <typename Val, typename Result_T = decltype(value ^ Val::value)>
	struct BitXor{
		using result = MetaInteger<Result_T, value ^ Val::value>;
	};
	
	struct BitFlip{
		using result = MetaInteger<T, ~value>;
	};
	 
	
	template <typename Val, typename Result_T = decltype(value + Val::value)>
	using add = typename Add<Val, Result_T>::result;
	template <typename Val, typename Result_T = decltype(value - Val::value)>
	using mns = typename Mns<Val, Result_T>::result;
	template <typename Val, typename Result_T = decltype(value * Val::value)>
	using mul = typename Mul<Val, Result_T>::result;
	template <typename Val, typename Result_T = decltype(value / Val::value)>
	using div = typename Div<Val, Result_T>::result;
	template <typename Val, typename Result_T = decltype(value % Val::value)>
	using rmd = typename Rmd<Val, Result_T>::result;
	
	using inc = typename Inc::result;
	using dec = typename Dec::result;
	
	template <typename Val, typename Result_T = decltype(value & Val::value)>
	using bit_and = typename BitAnd<Val, Result_T>::result;
	template <typename Val, typename Result_T = decltype(value | Val::value)>
	using bit_or  = typename BitOr<Val, Result_T>::result;
	template <typename Val, typename Result_T = decltype(value ^ Val::value)>
	using bit_xor  = typename BitXor<Val, Result_T>::result;

	using bit_flip = typename BitFlip::result;
	
	
	//A necessery interface for TypeList:
	struct GetData {
		static constexpr T data = Value;
	};
};

template <bool Value = false>
class MetaBoolean {
	public:
	static constexpr bool value = Value;


	template <typename Val>
	struct LogicAnd {
		using result = MetaBoolean<Value && Val::value>;
	};
	template <typename Val>
	struct LogicOr {
		using result = MetaBoolean<Value || Val::value>;
	};

	struct LogicNot {
		using result = MetaBoolean<!Value>;
	};
	
	template <typename Val>
	using logic_and = typename LogicAnd<Val>::result;
	template <typename Val>
	using logic_or  = typename LogicOr<Val>::result;

	using logic_not = typename LogicNot::result;
	

	struct GetData {
		static constexpr bool data = Value;
	};
};






template <char Value = '\0'>
using meta_char = MetaInteger<char, Value>;
template <unsigned char Value = 0>
using meta_uchar = MetaInteger<unsigned char, Value>;

template <int Value = 0>
using meta_int = MetaInteger<int, Value>;
template <unsigned int Value = 0>
using meta_uint = MetaInteger<unsigned int, Value>;

template <long Value = 0L>
using meta_long = MetaInteger<long, Value>;
template <unsigned long Value = 0UL>
using meta_ulong = MetaInteger<unsigned long, Value>;

template <long long Value = 0LL>
using meta_llong = MetaInteger<long long, Value>;
template <unsigned long long Value = 0ULL>
using meta_ullong = MetaInteger<unsigned long long, Value>;


template <bool Value = false>
using meta_bool = MetaBoolean<Value>;

using meta_null = MetaNull;



//tools

template <typename T>
using print_to_err = typename T::_;


template <typename... Ts>
struct ParameterPack {};
template <typename... Ts>
using pack = ParameterPack<Ts...>;

template <typename Source, template <typename...> class Destination>
struct CopyParameterPack {};
template <template <typename...> class SourceTempl, template <typename...> class Destination, typename... Ts>
struct CopyParameterPack<SourceTempl<Ts...>, Destination> {
	using result = Destination<Ts...>;
};
template <typename Source, template <typename...> class Destination>
using copy_pack = typename CopyParameterPack<Source, Destination>::result;


template <typename Pack>
struct IsNullPack {};
template <template <typename...> class PackType, typename... Ts>
struct IsNullPack<PackType<Ts...>> {
	using result = meta_bool<sizeof...(Ts) == 0>;
};
template <typename Pack>
using is_null_pack = typename IsNullPack<Pack>::result;


template <typename Condition, typename TrueBranch, typename FalseBranch>
struct MetaIf {};
template <typename TrueBranch, typename FalseBranch>
struct MetaIf<meta_bool<true>, TrueBranch, FalseBranch>  {using result = TrueBranch; };
template <typename TrueBranch, typename FalseBranch>
struct MetaIf<meta_bool<false>, TrueBranch, FalseBranch> {using result = FalseBranch;};
template <bool condition, typename TrueBranch, typename FalseBranch>
using meta_if = typename MetaIf<meta_bool<condition>, TrueBranch, FalseBranch>::result;

template <typename T>
struct MetaSwitch {
	template <typename K, typename V>
	struct Match {
		template <typename NextK, typename NextV>
		using match = Match<NextK, NextV>;
		
		template <typename SpareV>
		struct Spare {
			using result = SpareV;
		};
		template <typename SpareV>
		using spare = typename Spare<SpareV>::result;
		
	};
	template <typename V>
	struct Match<T, V> {
		template <typename NextK, typename NextV>
		using match = Match<T, V>;
		
		template <typename SpareV>
		struct Spare {
			using result = T;
		};
		template <typename SpareV>
		using spare = typename Spare<SpareV>::result;
		
	};
	template <typename K, typename V>
	using match = Match<K, V>;
	
};
template <typename T>
using meta_switch = MetaSwitch<T>;

//check two type if is the same.
template <typename T, typename U>
struct IsSame {
	using result = meta_bool<false>;
};
template <typename T>
struct IsSame<T, T> {
	using result = meta_bool<true>;
};
template <typename T, typename U>
using is_same = typename IsSame<T, U>::result;


template <template <typename...> class T, template <typename...> class U>
struct IsSameTemplate {
	using result = meta_bool<false>;
};
template <template <typename...> class T>
struct IsSameTemplate<T, T> {
	using result = meta_bool<true>;
};
template <template <typename...> class T, template <typename...> class U>
using is_same_templ = typename IsSameTemplate<T, U>::result;


}	//namespace meta

}	//namespace rais

#endif